//ostream << class using reflection
//$Id: stream_visitor.h 395 2010-03-26 14:01:03Z Oleg.Bulychov $
#pragma once

#include <string>
#include <iostream>

#include "src/heo/include/lf_endl.h"
#include "src/heo/include/reflection.h"
#include "src/heo/include/ptr_visit.h"

namespace meta
{
//__________________________________________________________________________________
class ostream_visitor:
    public meta::visitor< ostream_visitor >
{
public:
    static const int tab_size = 4;
    std::string padd_;
    std::basic_ostream<char, std::char_traits<char> >& out_;

    explicit ostream_visitor(std::basic_ostream<char, std::char_traits<char> >& out): out_(out)
    {}

    template<typename T, typename D>
    void visit_Array(T* pt, size_t N, char const* szName, D const& d)
    {
        if (*szName)
        {
            out_ << padd_.c_str() << szName << "[" << N << "] = {" << lf;
            padd_.resize(padd_.length() + tab_size, ' ');
        }
        for (size_t i = 0; i < N; ++i, ++pt)
            visit_T(*pt);
        if (*szName)
        {
            padd_.resize(padd_.length() - tab_size);
            out_ << padd_.c_str() << "}" << lf;
        }
    }

    template<typename T, typename D>
    void visit_Ptr(T* pt, char const* szName, D const& d)
    {
        if (*szName)
        {
            out_ << padd_.c_str() << "*" << szName << " = {" << lf;
            padd_.resize(padd_.length() + tab_size, ' ');
        }
        d.accept(pt, *this);
        if (*szName)
        {
            padd_.resize(padd_.length() - tab_size);
            out_ << padd_.c_str() << "}" << lf;
        }
    }

    template<typename T, typename D>
    void visit_Class(T& t, char const* szName, D const&)
    {
        if (!*szName)
            szName = T::get_Name();
        if ('/' == *szName)
            ++szName;
        if (*szName)
        {
            out_ << padd_.c_str() << "[" << szName << "] = {" << lf;
            padd_.resize(padd_.length() + tab_size, ' ');
        }
        t.accept(*this);
        if (*szName)
        {
            padd_.resize(padd_.length() - tab_size);
            out_ << padd_.c_str() << "}" << lf;
        }
    }

    template<typename T, typename D>
    void visit(T& t, char const* szName, D const& d)
    {
        out_ << padd_.c_str();
        if ('/' == *szName)
            ++szName;
        if (*szName)
             out_ << szName << "=";
        std::string s;
        char const* sz = meta::Value2String(t, s);
        out_ << sz << lf;
    }

    template<typename D>
    void visit(std::string& s, char const* szName, D const& d)
    {
        out_ << padd_.c_str();
        if (*szName)
             out_ << szName << "=";
        out_ << "'" << s << "'" << lf;
    }

    template<typename X, typename D>
    void visit(std::vector<X>& v, char const* szName, D const& d)
    {
        //:NOTE: vector<bool> may be totally broken and can't be treated as a C array
        //:use vector<char> instead
        if (*szName)
        {
            out_ << padd_.c_str() << szName << ", size=" << v.size() << ", {" << lf;
            padd_.resize(padd_.length() + tab_size, ' ');
        }
        for (size_t i = 0; i < v.size(); ++i)
        {
            visit_T(v[i]);
            if (i < v.size() - 1)
                out_ << lf;
        }
        if (*szName)
        {
            padd_.resize(padd_.length() - tab_size);
            out_ << padd_.c_str() << "}" << lf;
        }
    }

    template<typename T>
    void operator <<(T& t)
    {
        visit_T(t, T::get_Name());
    }

    //template<typename T>
    //void operator <<(T* t)
    //{
    //    visit_Ptr(t);
    //}
};
//__________________________________________________________________________________
}//meta
//__________________________________________________________________________________
REGISTER_VISITOR(meta::ostream_visitor)
//__________________________________________________________________________________
//EOF!
